iT邦幫忙

2024 iThome 鐵人賽

DAY 24
0

在開始之前,我們要再做一些詞彙的釐清:

  1. 在 Datomic 的官方文件裡,對資料庫寫入稱之為交易 (transaction)
  2. 在 Datomic 的官方文件裡,欄位綱要 (schema) 又稱之為屬性 (attribute)

要對 Datomic 資料庫做寫入,首先第一件事,必須先定義好綱要 (schema)。與 SQL 資料庫很大的一點不同是:「Datomic 並沒有專用的 DDL (Data Definition Language),因為 Datomic 的綱要也是資料,所以用一般的語法寫入即可。」

定義綱要

那該怎麼定義網要呢?這就要用到 Datomic 的內建屬性。此外,每一個屬性,都有三個必要的內建屬性。

比方說,下方我們定義了一個 :movie/title 的綱要,這個 :movie/title 之後就可以拿來當綱要使用。在定義它的時候,我們要指定三個必要的內建屬性:

  • :db/ident 定義它的名稱。
  • :db/valueType 定義它的資料型態。
  • :db/cardinality 定義它所對應的值是單一又或是多數。

上述三個 :db/ident, :db/valueType, :db/cardinality 就是三個必要的內建屬性。而 :db/doc 是可以選配的內建屬性。讀者可以發現,內建屬性的命名有一致性,一定會是 :db/$$$$ 的型式,這個 :db 的命名空間自然也是保留給內建屬性使用的。

{:db/ident :movie/title
 :db/valueType :db.type/string
 :db/cardinality :db.cardinality/one
 :db/doc "The title of the movie"}

:db/valueType 有哪些資料型態可以指定

下列都是可以用的資料型態:

(':db.type/bigdec'  | ':db.type/bigint'  | ':db.type/boolean' |
 ':db.type/bytes'   | ':db.type/double'  | ':db.type/float'   |
 ':db.type/instant' | ':db.type/keyword' | ':db.type/long'    |
 ':db.type/ref'     | ':db.type/string'  | ':db.type/symbol'  |
 ':db.type/tuple'   | ':db.type/uuid'    | ':db.type/uri')

其中,有 8 個比較常用:

  • :db.type/instant:表示時間,對應 java.util.Date
  • :db.type/uuid : UUID,對應 java.util.UUID
  • :db.type/ref :表示指標,可以指向別的資料實體 (entity)
  • :db.type/string':字串,對應 java.lang.String
  • :db.type/long :長整數
  • :db.type/float :浮點數 (float)
  • :db.type/double :浮點數 (double)
  • :db.type/boolean :布林值

特別注意一點::db.type/bytes 這個資料型態因為有設計上的考慮不周,已經被棄用了。

寫入真實的資料

這邊來看一個範例,先定義電影的屬性,然後寫入屬性、寫入電影的資料。

  • 定義屬性
(def movie-schema [{:db/ident :movie/title
                    :db/valueType :db.type/string
                    :db/cardinality :db.cardinality/one
                    :db/doc "The title of the movie"}

                   {:db/ident :movie/genre
                    :db/valueType :db.type/string
                    :db/cardinality :db.cardinality/one
                    :db/doc "The genre of the movie"}

                   {:db/ident :movie/release-year
                    :db/valueType :db.type/long
                    :db/cardinality :db.cardinality/one
                    :db/doc "The year the movie was released in theaters"}])

這邊,定義了三個自訂屬性:movie/title:movie/genra:movie/release-year

定義好了之後,我們就可以利用這三個自訂屬性來描述想放入資料庫的資料了。

  • 寫入屬性
@(d/transact conn movie-schema)

在這邊,conn 是資料庫的連線,d/transact 是寫入資料庫的 API ,而 movie-schema 則是前面定義好的屬性。

  • 寫入電影的資料
(def first-movies [{:movie/title "The Goonies"
                    :movie/genre "action/adventure"
                    :movie/release-year 1985}
                   {:movie/title "Commando"
                    :movie/genre "action/adventure"
                    :movie/release-year 1985}
                   {:movie/title "Repo Man"
                    :movie/genre "punk dystopia"
                    :movie/release-year 1984}])
                   
@(d/transact conn first-movies)                   

在這邊,first-movies 這個變數對應了電影的資料,仔細看,這邊寫入了三部電影,每部電影都恰好用三個之前定義好的自訂屬性加以描述。

註:從這邊開始,讀者如果去看官方文件時,可能會注意到 Datomic 有三個不同的版本:Datomic Cloud, Datomic Pro, Datomic Local。此外,Datomic 的 API 也有 Peer API 與 Client API 兩種。要解釋完差異與該如何選擇,故事有點長,已超出本系列文的範疇。本系列文只專注於 Datomic ProPeer API

交易資料 (tx-data) 的格式

Datomic 寫入資料庫 API d/transact 的第二個引數,它是 vector of tx-data 。讀者可以想成,交易資料 (tx-data) 是最小單位,它可以對應到一個又一個的資料原子 (datom)。而我們在寫入資料時,通常還是會一口氣寫入多個資料原子,所以 API 的第二個引數會是 vector of tx-data。

交易資料 (tx-data) 有兩種形式: map 形式或是 list 形式。兩種形式是等價的,之前的範例是 map 形式,map 形式是 list 形式的語法糖而已。所以我們要深入了解交易資料,還是要從 list 形式來著手。

先看 list 形式的交易資料的基本範例:

  • 設定 entity-id 的 attribute 為 value
[:db/add entity-id attribute value]
  • 取消 entity-id 的 attribute 為 value
[:db/retract entity-id attribute value?]

等價的表示法:

  • map 形式的交易資料,並且明確寫出 :db/id
[{:db/id item-1-id
  :line-item/product chocolate
  :line-item/quantity 1}
 {:db/id item-2-id
  :line-item/product whisky
  :line-item/quantity 2}]
  • list 形式的交易資料
[[:db/add item-1-id :line-item/product chocolate]
 [:db/add item-1-id :line-item/quantity 1]
 [:db/add item-2-id :line-item/product whisky]
 [:db/add item-2-id :line-item/quantity 2]]

暫時編碼 (Tempids)

這邊有一個很容易讓人困惑的:「當一筆資料還沒有寫入資料庫時,我們無法得知它對應的資料實體編碼 (entity id) 啊?那該怎麼填入類似上方例子裡的 item-1-iditem-2-id 呢?」

答案很簡單,就用一個臨時字串就好了,比方說:"temp-item-1" 。而這邊的臨時字串在 Datomic 的官方文件又稱之為暫時編碼 (Tempids)

Datomic 特有的交易語意

先來探討一下 SQL 資料庫的交易 (transaction) 語意。

多數的 SQL 資料庫系統由一系列更新組成交易,其中每個更新都會更改資料庫的狀態。根據所提供的隔離程度,交易也可能會看到來自其他交易的交錯更新,例如 a' 甚至可能不等於 b

BEGIN TRANSACTION
UPDATE ... ;; db state a -> db state a'
UPDATE ... ;; db state b -> db state b'
UPDATE ... ;; db state c -> db state c'
COMMIT

然而,Datomic 並非如此。Datomic 的交易定義為將一組資料原子 (datoms) 新增至資料庫。

參考資料

  1. Datomic Schema Reference
  2. Datomic Transaction Tutorial
  3. Datomic Transaction Data Reference
  4. Comparision with Updating Transactions

其它資源

  1. 歡迎訂閱 PruningSuccess 電子報,主要談論軟體開發、資料處理、資料分析等議題。
  2. 歡迎加入 Clojure 社群

上一篇
欄位綱要 (column schema)
下一篇
主鍵 (primary key) 與資料實體編碼 (entity id)
系列文
Datomic,內建事件溯源的資料庫。30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言